#include <kernel.h>
#include <pa4.h>
#include <buf.h>
//#include <disk.h>

extern int dskread(struct devsw *pdev, char *buffer, int block_no, int count);
extern int dskwrite(struct devsw *pdev, char *buffer, int block_no, int count);

extern int memncpy(void *dest, void *src, int num);
/*
 * Part A 3/4. buf_fetch()
 * buf_fetch() does:
 *     if there is a buffer that already store the block data of block_no,
 *         then return the buffer.
 *     otherwise
 *         read the block of block_no, store it on a buffer, and return the buffer.
 * 
 * parameters:
 *     pdev:		device descriptor
 *     block_no: 	a block number to read
 *     policy: 		buffer replacement policy
 */


int whichPolicy;
//********************************************************
void printBuffer()
{
dsk_buffer_p m= buf_head;
kprintf("BUFFER ENTRIES:  ");
while(m!=NULL)
        {
          kprintf("%d   ",m->block_no);
	  m=m->next;
	}
kprintf("  -- Buf_count: %d\n",buf_count);
}

//********************************************************
dsk_buffer_p findNode(int block_no)
{
	dsk_buffer_p m= buf_head;
	while(m!=NULL)
	{
		if(m->block_no==block_no)
		{
			return m;
		}
		m=m->next;
	}
m=NULL;
return m;
}

//*****************************************************
void evict(int num)
{
int i;
dsk_buffer_p penUltimate,tail;


for(i=0;i<num;i++)
{
tail=buf_head;
kprintf("Finding last node:\n");
while(1)
{
  kprintf(" %d ",tail->block_no);
  penUltimate=tail;
  tail=tail->next;
	if(tail->next==NULL) break;
}
penUltimate->next=NULL;
kprintf("\nPenUltimate Block is : %d \n",penUltimate->block_no);
kprintf("Evicting from buffer -block no: %d\n",tail->block_no);
if(tail->dirty==1)
dskwrite(tail->pdev,tail->data,tail->block_no,1);

freemem(tail,sizeof(struct buf) );
buf_count--;

}//For loopNumber of Blocks to be evicted!!

}//End of Evict

//*****************************************************
dsk_buffer_p readAndInsert(struct devsw *pdev,int block_no)
{
int i;
dsk_buffer_p tmp,target;
//Find if it needs to be WORD or char


if((buf_count+1+PA4_PREFETCH_SIZE)>PA4_BUFFER_SIZE)
{
evict(buf_count+1+PA4_PREFETCH_SIZE-PA4_BUFFER_SIZE);

}

  
char * buffer=getmem(128*(PA4_PREFETCH_SIZE+1));

dskread(pdev,buffer,block_no,PA4_PREFETCH_SIZE+1);

for(i=0;i<PA4_PREFETCH_SIZE+1;i++)
{
if(findNode(block_no+i)==NULL)
{
tmp=getmem(sizeof(struct buf));
  tmp->block_no=block_no+i;
  tmp->pdev=pdev;
  tmp->size=128;
  tmp->data=getmem(128);
  memncpy(tmp->data,buffer+(i*128),128);
  tmp->next=buf_head;
  tmp->dirty=0;
  buf_head=tmp;
if(i==0)
	target=tmp;
 kprintf("\tfetched block no: %d\n",tmp->block_no);
 buf_count++;
}
else
{
kprintf("\tAlready there so Not fetching block: %d\n", block_no+i);
}

}//for
kprintf("\tALL REQUIRED BLOCKS FETCHED or SKIPPED!!\n");
return target;
}
//********************************************************
int updateHead(int block_no)
{
dsk_buffer_p tmp,prev;
if(buf_head->block_no==block_no)
return 1;

tmp=buf_head;
while(tmp->block_no!=block_no)
{
prev=tmp;
tmp=tmp->next;
}

prev->next=tmp->next;
tmp->next=buf_head;
buf_head=tmp;
return 1;
}

//********************************************************
dsk_buffer_p buf_fetch(struct devsw *pdev, int block_no, int policy) {
STATWORD ps;
dsk_buffer_p target;

while(invalid[block_no]==1){
resched();
}
invalid[block_no]=1;



/*
if(0>block_no || block_no> 1024) )
{
restore(ps);
invalid[block_no]=0;
printBuffer();
return INVALID_BLOCK;
}*/



target=findNode(block_no);
if(target==NULL)
{
kprintf("Miss for Block number : %d\n",block_no);
}
else
{
kprintf("HIT for Block number : %d\n",block_no);
}



if(target!=NULL)//BLOCK HIT
{
if(policy!=POLICY_FIFO) updateHead(block_no);
invalid[block_no]=0;
printBuffer();
return target;
}

target=readAndInsert(pdev,block_no);
invalid[block_no]=0;
kprintf("Fetch Done!!!\n");
printBuffer();
return target;

}
